﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace TreeMap
{
	public class EntryManager
	{
		private static EntryManager MyInstance = new EntryManager();

		public static EntryManager Instance
		{
			get
			{
				return MyInstance;
			}
		}

		private List<string> MyCategories = new List<string>();
		private Dictionary<string, List<string>> MyCategoryValues = new Dictionary<string, List<string>>();
		private List<Entry> MyEntries = new List<Entry>();
		private List<EntryNode> MyRootNodes = new List<EntryNode>();

		public List<string> Categories
		{
			get
			{
				return this.MyCategories;
			}
		}
		public List<Entry> Entries
		{
			get
			{
				return this.MyEntries;
			}
		}
		public List<EntryNode> RootNodes
		{
			get
			{
				return MyRootNodes;
			}
		}

		public void LoadFile(string filePath)
		{
			this.MyEntries.Clear();
			this.MyCategories.Clear();
			this.MyCategoryValues.Clear();
			try
			{
				using (StreamReader Reader = new StreamReader(filePath))
				{
					string Line = Reader.ReadLine();
					this.MyCategories.AddRange(Line.Split(Properties.Settings.Default.CSVSeperator));
					foreach (string Category in this.MyCategories)
					{
						this.MyCategoryValues.Add(Category, new List<string>());
					}
					string[] Values;
					Entry CurrentEntry;
					while ((Line = Reader.ReadLine()) != null)
					{
						Values = Line.Split(Properties.Settings.Default.CSVSeperator);
						CurrentEntry = this.MyEntries.Find(e => e.HasSameCategories(Values));
						if (CurrentEntry == null)
						{
							CurrentEntry = new Entry(Values);
							this.MyEntries.Add(CurrentEntry);
							for (int i = 0; i < Values.Length; i++)
							{
								if (!this.MyCategoryValues[this.MyCategories[i]].Contains(Values[i]))
								{
									this.MyCategoryValues[this.MyCategories[i]].Add(Values[i]);
								}
							}
						}
						CurrentEntry.Amount++;
					}
				}
				//this.OrderEntries(this.MyCategories);
			}
			catch (Exception Ex)
			{
				Helper.HandleException(Ex, "Beim Laden des CSV-files trat ein Fehler auf. ", "Fehler beim Laden des CSV-Files");
			}
		}

		public void OrderEntries(List<string> categoryOrder)
		{
			if (categoryOrder != null && categoryOrder.Count > 0)
			{
				MyRootNodes.Clear();
				int Sum = 0;
				foreach (string CategoryValue in this.MyCategoryValues[categoryOrder[0]])
				{
					EntryNode Root = SetNode(null, CategoryValue, categoryOrder, 0);
					Sum += Root.Points;
					this.MyRootNodes.Add(Root);
				}
				foreach (EntryNode Root in this.MyRootNodes)
				{
					Root.Importance = (float)Root.Points / Sum;
					//PrintNode(Root, 0);
				}
			}
		}

		private void PrintNode(EntryNode node, int layer)
		{
			for (int i = 0; i < layer; i++) Console.Write("\t");
			Console.WriteLine(node.Category + " " + node.Value + " " + node.Points + "(" + node.Importance + ")");
			foreach (EntryNode Root in node.Children)
			{
				PrintNode(Root, layer + 1);
			}
		}

		private EntryNode SetNode(EntryNode parent, string categoryValue, List<string> categoryOrder, int index)
		{
			EntryNode Node = new EntryNode();
			Node.Value = categoryValue;
			Node.Category = categoryOrder[index];
			Node.Parent = parent;
			EntryNode ActualEntry = Node;
			int UnorderedIndex;
			string[] CategoryValues = new string[this.MyCategories.Count];
			do
			{
				UnorderedIndex = this.MyCategories.IndexOf(ActualEntry.Category);
				CategoryValues[UnorderedIndex] = ActualEntry.Value;
			}
			while ((ActualEntry = ActualEntry.Parent) != null);
			int NewIndex = index + 1;
			if (NewIndex < categoryOrder.Count)
			{
				UnorderedIndex = this.MyCategories.IndexOf(categoryOrder[NewIndex]);
				var Values = from entry in this.MyEntries
							 where entry.MatchesCategories(CategoryValues)
							 select entry.CategoryValues[UnorderedIndex];
				int Sum = 0;
				foreach (string Value in Values.Distinct())
				{
					EntryNode Child = this.SetNode(Node, Value, categoryOrder, NewIndex);
					Node.Children.Add(Child);
					Sum += Child.Points;
				}
				foreach (EntryNode Child in Node.Children)
				{
					Child.Importance = (float)Child.Points / Sum;
				}
				Node.Points = Sum;
			}
			else
			{
				Node.Points = this.MyEntries.Where(e => e.MatchesCategories(CategoryValues)).Sum(e => e.Amount);
			}
			return Node;
		}
	}
}
